import json
import re
import time
from threading import Thread

from src.settings import Settings
from src.user_data import UserData
from src.logger import Logger
from src.process import ExeControl
from src.utils.tool import Tool
from src.utils.method import Method
from src.bot.ws_handler import WsHandler


class Robot:
    def __init__(self, conf: UserData, logger: Logger, dls: ExeControl):
        self.__conf = conf
        self.__dls = dls
        self.__logger = logger
        self.__ws_handler = WsHandler(conf, logger)
        self.__data = self.__conf.dataDict
        self.__ini = self.__conf.iniCls
        self.owner = self.__ini.robot_owner
        self.group = self.__ini.robot_group
        self.group_cmd: list = self.__ini.robot_group_cmd
        # 内部参数
        self.online = True
        self.qq = None
        self.__thread_list = []
        self.init_data()

    # 发送消息至群聊
    def send_group_message(self, message: str, group_id=0) -> None:
        if not group_id:
            group_id = self.group
        data = {"group_id": group_id,
                "message": message}
        try:
            self.__ws_handler.do_cmd("send_group_msg", data)
        except Exception:
            return

    # 初始化机器人QQ号
    def get_bot_qq(self) -> None:
        try:
            back = self.__ws_handler.do_cmd("get_login_info")
            self.qq = back["data"]["user_id"]
        except Exception:
            return

    # 获取群成员列表
    def get_group_mem_list(self, group_id=0) -> list:
        if not group_id:
            group_id = self.group
        data = {"group_id": group_id}
        try:
            mem_info_list = self.__ws_handler.do_cmd("get_group_member_list", data)["data"]
            mem_id_list = []
            for mem_info in mem_info_list:
                mem_id_list.append(mem_info["user_id"])
            return mem_id_list
        except Exception:
            return []

    # 处理服务器日志转发至群中
    def deal_robot_log(self) -> None:
        combine_msg = self.__logger.get_robot_msg()
        if combine_msg.length:
            self.send_group_message(combine_msg.text)
        # （Minimouse添加了在发送消息的同时执行指令的功能）
        for cmd in self.__logger.get_cmd_list():
            self.do_cmd(cmd)

    # 处理QQ消息事件
    def deal_event_log(self) -> None:
        # 获取队列大小并执行
        size = self.__ws_handler.event.qsize()
        for _ in range(size):
            new_msg: dict = self.__ws_handler.event.get()
            try:
                # 判断是否为群消息
                if new_msg["post_type"] == "message":
                    if new_msg["message_type"] == "group":
                        # 判断是否为本群的消息
                        if new_msg["group_id"] != self.group:
                            continue
                        # 判断是否为自己发的消息
                        if new_msg["sender"]["user_id"] == self.qq:
                            continue
                        # 群聊消息处理CQ码
                        text: str = new_msg["raw_message"]
                        text = Method.deal_cq_tags(text)
                        # 分权限执行命令
                        for robot_cmd in self.group_cmd:
                            cmd_header: str = robot_cmd.get("识别头")
                            cmd_reg: str = robot_cmd.get("正则")
                            # 根据条件触发命令
                            # 空字符串识别头也允许匹配
                            if cmd_header is not None:
                                if not re.match(cmd_header, text):
                                    continue
                                # 存在识别头且匹配成功
                                plain: str = text.replace(cmd_header, "", 1).strip()
                            elif cmd_reg and re.findall(cmd_reg, text):
                                # 不存在识别头但正则匹配成功
                                plain: str = text
                            else:
                                continue
                            # 获取发送者信息
                            nickname: str = new_msg["sender"]["nickname"]
                            card_name: str = new_msg["sender"]["card"]
                            if card_name == "":
                                card_name = nickname
                            qq: int = new_msg["sender"]["user_id"]
                            # 替换占位符文本
                            cmd: str = robot_cmd["执行命令"].replace("%p", plain) \
                                .replace("%n", card_name).replace("%i", nickname) \
                                .replace("%q", str(qq))
                            if cmd_reg:
                                cmd_reg_res = re.findall(cmd_reg, plain)
                                cmd = Method.deal_placeholder(cmd, cmd_reg_res)
                            # 分权限处理
                            if robot_cmd.get("全员可用") or qq in self.owner \
                                    or qq in robot_cmd.get("指定成员可用", []):
                                self.__logger.add_log(f"[GLF机器人][RES] 执行命令: {cmd}")
                                self.count_and_do_cmd(robot_cmd, plain, str(qq), cmd)
                elif new_msg["post_type"] == "notice":
                    if new_msg["notice_type"] == "group_decrease":
                        user_id: int = new_msg["user_id"]
                        self.clear_data(str(user_id))
            except Exception as err:
                self.__logger.add_log(f"\n[GLF机器人][ERROR] 异常的消息！ 错误: {err}  消息json: {new_msg}\n")

    # 事件处理
    def deal_all(self) -> None:
        # 开始逻辑循环
        while self.online:
            time.sleep(0.8)
            # ws连接断开时挂起
            if not self.__ws_handler.alive:
                continue
            self.get_bot_qq()
            self.deal_robot_log()
            self.deal_event_log()

    def init_data(self) -> None:
        """初始化数据对象"""
        if not self.__data.get("cmdTimes"):
            self.__data["cmdTimes"] = {}
        for robot_cmd in self.group_cmd:
            if not robot_cmd.get("记录"):
                continue
            record = robot_cmd["记录"]
            if not self.__data["cmdTimes"].get(record):
                self.__data["cmdTimes"][record] = {}

    def search_data(self, record: str, user_id: str) -> list:
        """查询记录中指定用户的内容"""
        if self.__data["cmdTimes"][record].get(user_id):
            ret: list = self.__data["cmdTimes"][record][user_id]["记录值"]
            return ret
        return []

    def reset_data(self, record: str, qq: str) -> bool:
        """重置记录中指定用户的内容"""
        if self.search_data(record, qq):
            self.__data["cmdTimes"][record][qq] = {"次数": 0, "记录值": []}
            self.save_data()
            return True
        return False

    def clear_data(self, user_id: str):
        """重置记录并执行回调命令"""
        for robot_cmd in self.group_cmd:
            if not robot_cmd.get("记录"):
                continue
            record = robot_cmd["记录"]
            player_names: list = self.search_data(record, user_id)
            # 退群触发指令
            if robot_cmd.get("退群触发"):
                callback_cmd = robot_cmd.get("退群触发")
                if re.search("%r", callback_cmd):
                    for player_name in player_names:
                        cmd = callback_cmd.replace("%r", player_name)
                        self.do_cmd(cmd)
                else:
                    self.do_cmd(callback_cmd)
            if robot_cmd.get("退群重置") and player_names:
                self.send_group_message(f"{user_id}记录移除: {', '.join(player_names)}")
                self.reset_data(record, user_id)

    def save_data(self) -> None:
        """保存记录至文件"""
        self.save_data_to_file("cmdTimes", self.__data["cmdTimes"], "DLS/数据/命令次数记录.json")

    # 命令记录与执行
    def count_and_do_cmd(self, robot_cmd: dict, plain: str, user_qq: str, cmd: str) -> None:
        if robot_cmd.get("记录"):
            limit_times = robot_cmd.get("限制次数", 0)
            # 用户初始化
            if not self.__data["cmdTimes"][robot_cmd["记录"]].get(user_qq):
                self.__data["cmdTimes"][robot_cmd["记录"]][user_qq] = \
                    {"次数": 0, "记录值": []}
            user_dict: dict = self.__data["cmdTimes"][robot_cmd["记录"]][user_qq]
            # 判断与计数
            if limit_times == 0:
                self.send_group_message("记录还未启用！")
            elif user_dict["次数"] < limit_times:
                if self.do_cmd(cmd):
                    user_dict["次数"] += 1
                    user_dict["记录值"].append(plain)
                    self.send_group_message(f'ID: {user_qq}\n记录值: {", ".join(user_dict["记录值"])}')
                    # 保存记录至文件
                    self.__data["cmdTimes"][robot_cmd["记录"]][user_qq] = user_dict
                    self.save_data()
                else:
                    self.send_group_message("命令执行失败, 记录被撤销！")
            elif limit_times == -1 or user_qq in self.owner:
                self.do_cmd(cmd)
                user_dict["次数"] += 1
                user_dict["记录值"].append(plain)
                self.send_group_message(f'ID: {user_qq}\n记录值: {", ".join(user_dict["记录值"])}')
                # 保存记录至文件
                self.__data["cmdTimes"][robot_cmd["记录"]][user_qq] = user_dict
                self.save_data()
            else:
                self.send_group_message(f'命令次数已用完！\nID: {user_qq}\n记录值: {", ".join(user_dict["记录值"])}')
        else:
            self.do_cmd(cmd)

    # 移除指定记录
    def times_reset(self, record: str, qq: str) -> str:
        if self.reset_data(record, qq):
            return "移除记录成功"

    # 查询指定记录
    def times_search(self, record: str, qq: str) -> str:
        data = self.search_data(record, qq)
        if data:
            return f'ID: {qq}\n记录值: {", ".join(data)}'
        return f"未找到相关记录"

    # 清理退群人员的记录并执行回调命令
    def clear_left_member(self):
        mem_id_list = self.get_group_mem_list()
        for robot_cmd in self.group_cmd:
            if not robot_cmd.get("记录"):
                continue
            record = robot_cmd["记录"]
            all_data = self.__data["cmdTimes"][record]
            for user_id in all_data:
                if int(user_id) in mem_id_list:
                    continue
                player_names: list = self.search_data(record, user_id)
                # 退群触发指令
                if robot_cmd.get("退群触发"):
                    callback_cmd = robot_cmd.get("退群触发")
                    if re.search("%r", callback_cmd):
                        for player_name in player_names:
                            cmd = callback_cmd.replace("%r", player_name)
                            self.do_cmd(cmd)
                    else:
                        self.do_cmd(callback_cmd)
                if robot_cmd.get("退群重置") and player_names:
                    self.send_group_message(f"ID: {user_id}\n记录移除: {', '.join(player_names)}")
                    self.reset_data(record, user_id)
        self.send_group_message("清理已完成")

    # 文件的保存
    def save_data_to_file(self, data_key, data_content, file_path: str) -> None:
        file_path = Settings.WORK_PATH + "/" + file_path
        data_dict = {data_key: data_content}
        try:
            with open(file_path, "w", encoding="utf-8") as f:
                f.write(json.dumps(data_dict, sort_keys=True, indent=4, ensure_ascii=False))
        except Exception as err:
            self.__logger.add_log(f"[GLF机器人][ERROR] 文件{file_path}保存失败！错误原因: {err}！")

    # 执行命令
    def do_cmd(self, command: str) -> bool:
        if command == "start":
            self.__dls.exeStart()
        elif command == "stop":
            self.__dls.exeStop()
        elif command == "status":
            self.send_group_message(Tool.get_status())
        elif re.match("group", command):
            group_text = command.replace("group", "", 1).strip()
            self.__logger.add_robot_msg(group_text)
        elif re.match("cmd", command):
            command = command.replace("cmd", "", 1).strip()
            return self.__dls.executeCmd(command)
        elif re.match("chatJava", command):
            msg_text = command.replace("chatJava", "", 1).strip()
            raw_json = {"text": msg_text}
            command = f"tellraw @a {json.dumps(raw_json)}".replace("\'", "\"")
            return self.__dls.executeCmd(command)
        elif re.match("chat", command):
            msg_text = command.replace("chat", "", 1).strip()
            raw_json = {"rawtext": [{"text": msg_text}]}
            command = f"tellraw @a {json.dumps(raw_json)}".replace("\'", "\"")
            return self.__dls.executeCmd(command)
        elif re.match("motdpe", command):
            command = command.replace("motdpe", "", 1).strip()
            self.send_group_message(Tool.motd_pe(command))
        elif re.match("blackbe", command):
            command = command.replace("blackbe", "", 1).strip()
            self.send_group_message(Tool.black_be(command))
        elif re.match("xuid", command):
            command = command.replace("xuid", "", 1).strip()
            self.send_group_message(Tool.xuid_search(command))
        elif re.match("timesReset", command):
            command = command.replace("timesReset", "", 1).strip()
            args = command.split(" ")
            if len(args) != 2:
                self.send_group_message("请指定查询内容")
                return False
            record, qq = args
            self.send_group_message(self.times_reset(record, qq))
        elif re.match("timesSearch", command):
            command = command.replace("timesSearch", "", 1).strip()
            args = command.split(" ")
            if len(args) != 2:
                self.send_group_message("请指定查询内容")
                return False
            record, qq = args
            self.send_group_message(self.times_search(record, qq))
        elif re.match("clearLeftMem", command):
            self.clear_left_member()
        elif re.match("startBak", command):
            self.__dls.bak_world()
        elif re.match("dlsload", command):
            self.__conf.reload()
            self.__logger.reload()
            self.__dls.reload()
            self.reload()
        return True

    # 启动线程
    def start(self) -> None:
        # 未设定启用时退出
        if not self.__ini.robot_on:
            return
        th = Thread(target=self.deal_all, daemon=True)
        th.start()
        self.__thread_list.append(th)
        self.__ws_handler.start()
        self.__logger.add_log(f"[GLF机器人] 机器人已启用！")

    # 关闭线程
    def stop(self) -> None:
        # 未设定启用时退出
        if not self.__ini.robot_on:
            return
        # 关闭线程
        self.online = False
        for th in self.__thread_list:
            th.join()
        self.__thread_list.clear()

    # 重加载配置
    def reload(self) -> None:
        self.__ini = self.__conf.iniCls
        self.__data = self.__conf.dataDict
        self.owner = self.__ini.robot_owner
        self.group = self.__ini.robot_group
        self.group_cmd = self.__ini.robot_group_cmd
        self.init_data()
        self.__ws_handler.reload()
